﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using EventbriteService.Linq.Extensibility;

namespace Eventbrite.LinqProvider.Translators
{
    public class QueryTranslator: Helpers.ExpressionVisitor
    {
        WhereTranslator _where;
        OrderByTranslator _orderBy;
        TakeTranslator _take;
        ProjectionTranslator _select;
        KeywordsTranslator _keywords;

        bool _count;

       
        public QueryTranslator() { }

        public RequestInfo Translate(Expression expression)
        {
            Visit(expression);
            return ConvertToUrlrequest();
        }

        private RequestInfo ConvertToUrlrequest()
        {
            StringBuilder sb = new StringBuilder();
            // where translation
            sb.Append(_where.GetRequestParams());
          
            // keywords translation
            if (_keywords != null)
            {
                sb.Append("&q=");
                sb.Append(_keywords.KeywordParams());
            }
           
                      
            // order by translation
            if (_orderBy != null)
            {
                var orderBy = _orderBy.OrderByClouse;
                if (!String.IsNullOrEmpty(orderBy))
                {
                    sb.Append("&sort_by=");
                    sb.Append(orderBy);
                }
            }

            if (_count)
            {
                sb.Append("&count_only=true");
            }
            return new RequestInfo
                                {
                                    UrlRequest = sb.ToString(),
                                    LambdaExpression = _select == null ? null : _select.ProjectionLambda,
                                    IsDefaultSet = _take != null ? _take.IsDefaultSet : false,
                                    IsCount = _count
                                };
        }

        protected override Expression VisitMethodCall(MethodCallExpression mc)
        {
            Type declaringType = mc.Method.DeclaringType;
            if (declaringType != typeof(Queryable) && 
                declaringType != typeof(EventbriteQueryable))
                throw new NotSupportedException(
                  "The type for the operator is not Queryable!");
            switch (mc.Method.Name)
            {
                case "Select":
                   var selectLambda = StripQuotes(mc.Arguments[1]) as LambdaExpression;
                    if (selectLambda != null)
                       VisitSelect(selectLambda);
                    break;
                case ("Where"):
                    LambdaExpression bodyWhere = StripQuotes(mc.Arguments[1]) as LambdaExpression;
                    if (bodyWhere != null)
                        VisitWhere(bodyWhere);
                    break;
                case("OrderBy"):
                    LambdaExpression bodyOrder = StripQuotes(mc.Arguments[1]) as LambdaExpression;
                    if(bodyOrder != null)
                        VisitOrderBy(bodyOrder);
                    break;
                
                case "SearchKeywords":
                    VisitSearchKeywords(mc.Arguments[1]);
                    break;
                case "AndSearchKeywords":
                    VisitAndSearchKeywords(mc.Arguments[1]);
                    break;
               
               default:
                    throw new NotSupportedException(string.Format( "{0} is not supported", mc.Method.Name ));
           }

            Visit(mc.Arguments[0]);
            return mc;
        }

        private void VisitCount()
        {
            if (_count)
            {
                throw new NotSupportedException(
                  "You cannot have more than 1 Count in the expression");
            }
            _count = true;
        }


       private void VisitWhere(LambdaExpression expression)
        {
            if (_where != null)
                throw new NotSupportedException(
                  "You cannot have more than 1 Where in the expression");
          
            _where = new WhereTranslator();
            _where.Translate(expression);

        }

        private void VisitSelect(LambdaExpression predicate)
        {
            if (_select != null)
                throw new NotSupportedException("You cannot have more than 1 Select in the expression");

            _select = new ProjectionTranslator();
            _select.Translate(predicate);
        }

        private void VisitSearchKeywords(Expression expression)
        {
            if (_keywords != null)
                throw new NotSupportedException("You cannot have more than 1 SearchKeyords in the expression");

            _keywords = new KeywordsTranslator();
            _keywords.Translate(expression);
        }

        private void VisitAndSearchKeywords(Expression expression)
        {
            if (_keywords != null)
                throw new NotSupportedException("You cannot have more than 1 SearchKeyords in the expression");

            _keywords = KeywordsTranslator.GetAndSearchTranslator();
            _keywords.Translate(expression);
        }

        private void VisitTake(Expression takeValue)
        {
            if (_take != null)
                throw new NotSupportedException("You cannot have more than 1 Take/First/FirstOrDefault in the expression");

            _take = new TakeTranslator();
            _take.Translate(takeValue);
        }

        private void VisitFirst(bool useDefault)
        {
            if (_take != null)
                throw new NotSupportedException("You cannot have more than 1 Take/First/FirstOrDefault in the expression");

            _take = TakeTranslator.GetFirstTranslator();
            _take.IsDefaultSet = useDefault;
        }

        private void VisitOrderBy(LambdaExpression expression)
        {
            if (_orderBy != null)
                throw new NotSupportedException("You cannot have more than 1 OrderBy in the expression");

            _orderBy = new OrderByTranslator();
            _orderBy.Translate(expression);
        }

        private static Expression StripQuotes(Expression e)
        {
            while (e.NodeType == ExpressionType.Quote)
            {
                e = ((UnaryExpression)e).Operand;
            }
            return e;
        }
    }
}
